package com.xue.impl.sqlBase.parser;

import java.util.function.Function;

import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.atn.PredictionMode;
import org.antlr.v4.runtime.misc.ParseCancellationException;

import com.xue.antlr.sqlBase.SqlBaseLexer;
import com.xue.antlr.sqlBase.SqlBaseParser;
import com.xue.impl.sqlBase.tree.Node;
import com.xue.impl.sqlBase.tree.Statement;

public class SqlParser {

	/**
	 * Consider using {@link #createStatement(String, ParsingOptions)}
	 */
	public Statement createStatement(String sql) {
		return createStatement(sql, new ParsingOptions());
	}

	public Statement createStatement(String sql, ParsingOptions parsingOptions) {
		return (Statement) invokeParser("statement", sql, SqlBaseParser::singleStatement, parsingOptions);
	}

	private Node invokeParser(String name, String sql, Function<SqlBaseParser, ParserRuleContext> parseFunction,
			ParsingOptions parsingOptions) {
		try {
			// SqlBaseLexer lexer = new SqlBaseLexer(new CaseInsensitiveStream(new
			// ANTLRInputStream(sql)));
			SqlBaseLexer lexer = new SqlBaseLexer(new CaseInsensitiveStream(CharStreams.fromString(sql)));
			CommonTokenStream tokenStream = new CommonTokenStream(lexer);
			SqlBaseParser parser = new SqlBaseParser(tokenStream);

			lexer.removeErrorListeners();

			parser.removeErrorListeners();

			ParserRuleContext tree;
			try {
				// first, try parsing with potentially faster SLL mode
				parser.getInterpreter().setPredictionMode(PredictionMode.SLL);
				tree = parseFunction.apply(parser);
			} catch (ParseCancellationException ex) {
				// if we fail, parse with LL mode
				// tokenStream .reset(); // rewind input stream
				tokenStream.seek(0); // rewind input stream
				parser.reset();

				parser.getInterpreter().setPredictionMode(PredictionMode.LL);
				tree = parseFunction.apply(parser);
			}

			return new AstBuilder(parsingOptions).visit(tree);
		} catch (StackOverflowError e) {
			throw new ParsingException(name + " is too large (stack overflow while parsing)");
		}
	}
}
